package com.ybbase.framework.controller.flow.base;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ybbase.framework.base.model.ResponseBean;
import com.ybbase.framework.common.constant.CommonResultCode;
import com.ybbase.framework.common.constant.FlowConstant;
import com.ybbase.framework.common.util.DateUtil;
import com.ybbase.framework.common.util.EncryptUtil;
import com.ybbase.framework.model.po.flow.FlowExample;
import com.ybbase.framework.model.po.flow.InstanceRecord;
import com.ybbase.framework.model.vo.BusinessEntityVO;
import com.ybbase.framework.service.flow.BaseFlowService;
import com.ybbase.framework.service.flow.InstanceRecordService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.Model;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @author cg
 * @date 2019-08-27 09:06
 * @DEC 流程实例管理
 */
@RestController
@RequestMapping("/process")
@Api(value = "流程实例管理", tags = "流程实例管理")
public class ProcesssInstanceController {

    private static final Logger LOGGER = LoggerFactory.getLogger(ProcesssInstanceController.class);
    private static final String FLOW_PROCESS_TYPE_ACTIVE = "active";
    private static final String FLOW_PROCESS_TYPE_SUSPEND = "suspend";

    @Autowired
    ObjectMapper objectMapper;

    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private InstanceRecordService instanceRecordService;
    /**
     * 定义流程引擎
     */
    @Resource
    private ProcessEngine processEngine;
    @Autowired
    private BaseFlowService baseFlowService;

    /**
     * 根据模型ID和标记获取流程实例列表带分页
     *
     * @param flowExample 流程实例
     * @return
     * @throws
     * @author cg
     * @date 2019-08-15 10:44
     * @since
     */
    @GetMapping("/getProcessList")
    @ApiOperation(value = "根据模型ID和标记获取流程实例列表带分页", notes = "根据模型ID和标记获取流程实例列表带分页", response = ResponseBean.class)
    @ApiImplicitParams({
            @ApiImplicitParam(name = "flowExample", value = "流程实例", required = true, dataType = "FlowExample")
    })
    public ResponseBean getProcessList(FlowExample flowExample, @RequestParam(name = "page", defaultValue = "1") int pageIndex,
                                       @RequestParam(name = "rows", defaultValue = "20") int rows) {
        RepositoryService repositoryService = processEngine.getRepositoryService();
        Model model = repositoryService.createModelQuery().modelId(flowExample.getModelId()).singleResult();
        if (StringUtils.isEmpty(model.getDeploymentId())) {
            return new ResponseBean(CommonResultCode.FAIL.getCode(), "模型未进行部署，没有可查询信息");
        }
        Deployment deployment = repositoryService.createDeploymentQuery().deploymentId(model.getDeploymentId()).singleResult();
        List<FlowExample> flowExampleList = new ArrayList<>();
        Long count = 0L;
        if (flowExample.getFlag() == 0) {
            //正在运行的实例
            List<ProcessInstance> list = processEngine.getRuntimeService().createProcessInstanceQuery().deploymentId(deployment.getId())
                    .orderByProcessInstanceId().desc().listPage((pageIndex - 1) * rows, rows);
            count = processEngine.getRuntimeService().createProcessInstanceQuery().deploymentId(deployment.getId()).count();
            if (list != null && list.size() > 0) {
                for (ProcessInstance processInstance : list) {
                    flowExampleList.add(this.getFlowExample(flowExample.getFlag(), processInstance, null, deployment));
                }
            }
            if(flowExampleList!=null&&flowExampleList.size()>0){
                for(FlowExample fe:flowExampleList){
                    String flowId = fe.getFlowDefinitionId().split(":")[0];
                    QueryWrapper<InstanceRecord> queryWrapper = new QueryWrapper<>();
                    queryWrapper.lambda().eq(InstanceRecord::getRunProcessInstanceId, fe.getFlowId());
                    queryWrapper.lambda().eq(InstanceRecord::getRunDefinitionId, flowId);
                    queryWrapper.lambda().eq(InstanceRecord::getIsFinish, 0);
                    InstanceRecord instanceRecord =instanceRecordService.getOne(queryWrapper);
                    if(flowId.equals(FlowConstant.OPEN_ACCOUNT_FLOW) ){
                        fe.setPath("/openAccount/bill/open-bill-info/id="+instanceRecord.getBusinessId());
                    }else if (flowId.equals(FlowConstant.CASH_ORDER_FLOW) ){
                        fe.setPath("/cashOrder/bill/cash-bill-info/id="+instanceRecord.getBusinessId());
                    }
                }
            }
        } else {
            //已经结束的实例
            List<HistoricProcessInstance> list = processEngine.getHistoryService().createHistoricProcessInstanceQuery().deploymentId(deployment.getId()).finished()
                    .orderByProcessInstanceId().desc().listPage(pageIndex, rows);
            count = processEngine.getHistoryService().createHistoricProcessInstanceQuery().deploymentId(deployment.getId()).finished().count();
            if (list != null && list.size() > 0) {
                for (HistoricProcessInstance processInstance : list) {
                    flowExampleList.add(this.getFlowExample(flowExample.getFlag(), null, processInstance, deployment));
                }
            }
            if(flowExampleList!=null&&flowExampleList.size()>0){
                for(FlowExample fe:flowExampleList){
                    String flowId = fe.getFlowDefinitionId().split(":")[0];
                    QueryWrapper<InstanceRecord> queryWrapper = new QueryWrapper<>();
                    queryWrapper.lambda().eq(InstanceRecord::getRunProcessInstanceId, fe.getFlowId());
                    queryWrapper.lambda().eq(InstanceRecord::getRunDefinitionId, flowId);
                    queryWrapper.lambda().eq(InstanceRecord::getIsFinish, 1);
                    InstanceRecord instanceRecord =instanceRecordService.getOne(queryWrapper);
                    if(flowId.equals(FlowConstant.OPEN_ACCOUNT_FLOW) ){
                        fe.setPath("/openAccount/bill/open-bill-info/id="+instanceRecord.getBusinessId());
                    }else if (flowId.equals(FlowConstant.CASH_ORDER_FLOW) ){
                        fe.setPath("/cashOrder/bill/cash-bill-info/id="+instanceRecord.getBusinessId());
                    }
                }
            }
        }

        Page<FlowExample> page = new Page(pageIndex, rows);
        page.setTotal(count);
        page.setCurrent(pageIndex);
        page.setSize(rows);
        page.setRecords(flowExampleList);
        return new ResponseBean(CommonResultCode.SUCCESS.getCode(), CommonResultCode.SUCCESS.getMsg(), page);
    }

    /**
     * 根据实例定义的编码查询图片资源
     *
     * @param processDefinitionId 实例定义的编码
     * @return
     * @throws
     * @author cg
     * @date 2019-08-27 11:40
     * @since
     */
    @GetMapping(value = "/getImage")
    @ApiOperation(value = "根据实例定义的编码查询图片资源", notes = "根据实例定义的编码查询图片资源")
    @ApiImplicitParam(name = "processDefinitionId", value = "流程定义编码", required = true, dataType = "String")
    public void getImage(String processDefinitionId, HttpServletResponse response) {
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(processDefinitionId).singleResult();
        String resourceName = processDefinition.getDiagramResourceName();
        InputStream resourceAsStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), resourceName);
        byte[] b = new byte[1024*100];
        int len = -1;
        try {
            while ((len = resourceAsStream.read(b, 0, 1024)) != -1) {
                response.getOutputStream().write(b, 0, len);
            }
            response.getOutputStream().close();
        } catch (Exception e) {
            e.printStackTrace();
            LOGGER.error(e.getMessage());
        }
    }

    /**
     * 查看流程图的当前活动节点
     *
     * @param processDefinitionId 定义实例ID
     * @return
     * @throws
     * @author cg
     * @date 2019-09-06 16:07
     * @since
     */
    @GetMapping(value = "/getCurrentView")
    @ApiOperation(value = "查看流程图的当前活动节点", notes = "查看流程图的当前活动节点")
    @ApiImplicitParam(name = "processDefinitionId", value = "定义实例ID", required = true, dataType = "String")
    public ResponseBean getCurrentView(String processDefinitionId) {
        ProcessDefinitionEntity pdm = (ProcessDefinitionEntity) repositoryService.getProcessDefinition(processDefinitionId);
        ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processDefinitionId).singleResult();
        ActivityImpl ai = pdm.findActivity(pi.getActivityId());
        List<Integer> list = new ArrayList<>();
        list.add(ai.getX());
        list.add(ai.getY());
        list.add(ai.getWidth());
        list.add(ai.getHeight());
        return new ResponseBean(CommonResultCode.SUCCESS.getCode(), CommonResultCode.SUCCESS.getMsg(), list);
    }


    /**
     * 根据实例ID对实例进行挂起或激活
     *
     * @param state             类型 active激活 suspend挂起
     * @param processInstanceId 实例ID
     * @return ResponseBean
     * @throws
     * @author cg
     * @date 2019-08-27 11:41
     * @since
     */
    @GetMapping(value = "/updateState")
    @ApiOperation(value = "根据实例ID对实例进行挂起或激活", notes = "根据实例ID对实例进行挂起或激活", response = ResponseBean.class)
    @ApiImplicitParams({
            @ApiImplicitParam(name = "processInstanceId", value = "实例ID", required = true, dataType = "String"),
            @ApiImplicitParam(name = "state", value = "类型:active(激活) suspend(挂起)", required = true, dataType = "String")
    })
    public ResponseBean updateState(String state, String processInstanceId) {
        int ints = baseFlowService.updateStatus(state, processInstanceId, new BusinessEntityVO());
        if (ints == 1) {
            return new ResponseBean(CommonResultCode.SUCCESS.getCode(), "已激活ID为[" + processInstanceId + "]的流程实例。", null);
        } else {
            runtimeService.suspendProcessInstanceById(processInstanceId);
            return new ResponseBean(CommonResultCode.SUCCESS.getCode(), "已挂起ID为[" + processInstanceId + "]的流程实例。", null);
        }
    }

    /**
     * 根据实例ID删除实例
     *
     * @param processInstanceId 实例ID
     * @return
     * @throws
     * @author cg
     * @date 2019-08-27 11:42
     * @since
     */
    @DeleteMapping("/removeProcessInstance")
    @ApiOperation(value = "根据实例ID删除实例", notes = "根据实例ID删除实例", response = ResponseBean.class)
    @ApiImplicitParam(name = "processInstanceId", value = "实例ID", required = true, dataType = "String")
    public ResponseBean removeProcessInstance(String processInstanceId) {
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        if (processInstance != null) {
            return new ResponseBean(CommonResultCode.FAIL.getCode(), "当前有进行的实例不能进行删除");
        } else {
            runtimeService.deleteProcessInstance(processInstanceId, "删除成功！");
            return new ResponseBean(CommonResultCode.SUCCESS.getCode(), "删除实例" + CommonResultCode.SUCCESS.getMsg());
        }
    }

    /**
     * 获取流程实例
     *
     * @param flag                    类型
     * @param processInstance         实例
     * @param historicProcessInstance 历史实例
     * @param deployment              部署实例
     * @author cg
     * @date 2019-08-29 14:42
     */
    private FlowExample getFlowExample(Integer flag, ProcessInstance processInstance, HistoricProcessInstance historicProcessInstance, Deployment deployment) {
        FlowExample flowExample = new FlowExample();
        if (flag == 0) {
            flowExample.setFlowId(processInstance.getId());
            flowExample.setFlowDefinitionId(processInstance.getProcessDefinitionId());
            flowExample.setFlowName(processInstance.getProcessDefinitionName());
            flowExample.setVersion(processInstance.getProcessDefinitionVersion() + "");
            flowExample.setStartTime(DateUtil.getTime(deployment.getDeploymentTime() != null ? deployment.getDeploymentTime() : new Date()));
            flowExample.setFlowKey(processInstance.getProcessDefinitionKey());
            flowExample.setFlowCode(processInstance.getDeploymentId());
            flowExample.setEndTime("");
            flowExample.setIsHangUp(processInstance.isSuspended());
            flowExample.setFlowDefinitionIdKey(EncryptUtil.getInstance().sha1(processInstance.getProcessDefinitionId()));
        } else {
            flowExample.setFlowId(historicProcessInstance.getId());
            flowExample.setFlowDefinitionId(historicProcessInstance.getProcessDefinitionId());
            flowExample.setFlowName(historicProcessInstance.getProcessDefinitionName());
            flowExample.setVersion(historicProcessInstance.getProcessDefinitionVersion() + "");
            flowExample.setStartTime(DateUtil.getTime(historicProcessInstance.getStartTime() != null ? historicProcessInstance.getStartTime() : new Date()));
            flowExample.setFlowKey(historicProcessInstance.getProcessDefinitionKey());
            flowExample.setFlowCode(historicProcessInstance.getDeploymentId());
            flowExample.setEndTime(DateUtil.getTime(historicProcessInstance.getEndTime() != null ? historicProcessInstance.getEndTime() : new Date()));
            flowExample.setFlowDefinitionIdKey(EncryptUtil.getInstance().sha1(historicProcessInstance.getProcessDefinitionId()));
        }
        return flowExample;
    }

}
